﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Logging;

using Microsoft.Cci;
using PartialExpressionCompleter.Query;

namespace PartialExpressionCompleter.Experiment
{
    abstract class ExpressionExperiment<E> : Experiment where E:IExpression
    {
        protected bool computeTypeMeanings = true && ScoreSwitches.IsEnabled(ScoreTypes.AbstractTypeDistance);

        public ExpressionExperiment(IAssembly unit)
            : base(unit)
        { }

        protected abstract void DoExpressionAction(QueryContext<E> context);
        protected abstract IEnumerable<SavedExpression<E>> GetAllSavedExpressions(AbstractTypeInferencer inferencer);
        protected virtual Boolean IsInterestingExpression(E expression)
        {
            return true;
        }
        public override void RunExperiment()
        {
            Logger.PrintLine("Starting experiment on " + unit.Location + "...");
            Logger.PrintLine();
            var typeInferencer = new AbstractTypeInferencer(unit, computeTypeMeanings);
            Dictionary<IdentifiedTypeTerm, TypeTerm> typeMeanings = null;
            IMethodDefinition lastMethod = null;
            foreach (var savedExp in GetAllSavedExpressions(typeInferencer))
            {
                if (IsInterestingExpression(savedExp.Expression))
                {
                    try
                    {
                        IMethodDefinition containingMethod = savedExp.ContainingMethod;
                        if (computeTypeMeanings
                            && (!containingMethod.Equals(lastMethod)
                            || lastMethod == null || typeMeanings == null))
                        {
                            var startTi = System.DateTime.Now;
                            typeMeanings = typeInferencer.GetTermMeaningsWithout(containingMethod);
                            Logger.PrintLine("Time elapsed for type constraints (seconds): "
                                + (DateTime.Now - startTi).TotalSeconds);
                            Logger.PrintLine();
                        }
                        lastMethod = containingMethod;

                        ITypeDefinition containingType = containingMethod.ContainingTypeDefinition;
                        var context = new QueryContext<E>(containingType,
                            savedExp, typeMeanings);
                        DoExpressionAction(context);
                    }
                    catch (Exception e)
                    {
                        Logger.PrintLine("Something went wrong: " + e);
                        Logger.PrintLine(e.StackTrace);
                    }
                }
            }
        }
    }
}

